#!/bin/bash
stty sane
stty erase ^H

# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
    echo "错误：此脚本必须以 root 权限运行！"
    echo "请使用 'sudo -i' 切换到 root 用户后重试。"
    exit 1
fi

# 初始化环境
init_env() {
    # 恢复终端设置
    stty sane
    stty erase ^H 2>/dev/null
    
    # 颜色定义
    export RED='\033[0;31m'
    export GREEN='\033[0;32m'
    export YELLOW='\033[0;33m'
    export NC='\033[0m'
}

# 检查root权限
check_root() {
    if [ "$(id -u)" -ne 0 ]; then
        echo -e "${RED}错误：此脚本必须以 root 权限运行！${NC}"
        echo -e "请使用 ${YELLOW}'sudo -i'${NC} 切换到 root 用户后重试。"
        exit 1
    fi
}

# 配置文件路径
CONFIG_DIR="/etc/auth_system"
CONFIG_FILE="$CONFIG_DIR/oauser_config.dat"
MACHINE_ID_FILE="/etc/machine-id"
HOSTID=$(hostid)

# 创建配置目录（如果不存在）
mkdir -p "$CONFIG_DIR"

# 获取机器唯一标识
get_machine_identifier() {
    local machine_id=""
    
    if [[ -f "$MACHINE_ID_FILE" ]]; then
        machine_id=$(cat "$MACHINE_ID_FILE" 2>/dev/null | head -c 32 | tr -d '\0')
    fi
    
    # 如果machine-id不存在或太短，使用hostid和其他系统信息
    if [[ -z "$machine_id" || ${#machine_id} -lt 12 ]]; then
        machine_id="${HOSTID}-$(uname -n | md5sum | head -c 16 | tr -d '\0')"
    fi
    
    # 确保有足够长度的标识符
    echo -n "$machine_id" | sha256sum | awk '{print $1}' | head -c 32 | tr -d '\0'
}

# 生成加密密钥
generate_encryption_key() {
    local machine_id=$(get_machine_identifier)
    echo -n "${machine_id}${HOSTID}" | sha512sum | awk '{print $1}' | tr -d '\0'
}

# 加密数据 - 安全处理null字节
encrypt_data() {
    local data="$1"
    local key=$(generate_encryption_key)
    
    # 使用base64先编码，再加密，避免null字节问题
    local base64_data=$(echo -n "$data" | base64 -w 0)
    echo -n "$base64_data" | openssl enc -aes-256-cbc -salt -pass pass:"$key" -base64 2>/dev/null | tr -d '\0'
}

# 解密数据 - 安全处理null字节
decrypt_data() {
    local encrypted_data="$1"
    local key=$(generate_encryption_key)
    
    # 先解密，再base64解码
    local decrypted_base64=$(echo -n "$encrypted_data" | openssl enc -d -aes-256-cbc -pass pass:"$key" -base64 2>/dev/null | tr -d '\0')
    
    if [[ -n "$decrypted_base64" ]]; then
        echo -n "$decrypted_base64" | base64 -d 2>/dev/null | tr -d '\0'
    else
        return 1
    fi
}

# 生成数字签名
generate_signature() {
    local data="$1"
    echo -n "$data" | sha256sum | awk '{print $1}' | tr -d '\0'
}

# 验证数字签名
verify_signature() {
    local data="$1"
    local signature="$2"
    local expected_signature=$(generate_signature "$data")
    
    [[ "$signature" == "$expected_signature" ]]
}

# 安全读取文件内容（处理null字节）
safe_read_file() {
    local file="$1"
    # 使用tr移除null字节和其他不可打印字符
    cat "$file" 2>/dev/null | tr -d '\0' | tr -cd '\11\12\15\40-\176'
}

# 安全写入文件内容
safe_write_file() {
    local content="$1"
    local file="$2"
    
    # 移除null字节后写入
    echo -n "$content" | tr -d '\0' > "$file"
}

# 定义远程用户数据URL
USER_DATA_URL="https://gitee.com/banban1000/docker/raw/master/oa/oauser.txt"

# 加载用户数据
load_users() {
    local temp_file=$(mktemp)
    
    # 静默下载用户数据
    if ! curl --connect-timeout 15 --retry 2 -fsSL "$USER_DATA_URL" > "$temp_file"; then
        echo -e "${RED}错误：用户数据下载失败！${NC}"
        rm -f "$temp_file"
        exit 1
    fi

    # 清空原有用户数据
    unset users
    declare -gA users

    # 解析用户数据
    while IFS= read -r line; do
        # 移除注释和空格
        line=$(echo "$line" | sed -e 's/#.*$//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d '\0')
        
        if [[ "$line" =~ ^\[[\"\']?([^]\"\']+)[\"\']?\]=\"([^\|]+\|[^\|]+\|[0-9]+) ]]; then
            local username=$(echo "${BASH_REMATCH[1]}" | tr -d '"'"'" | tr -d '\0')
            users["$username"]="${BASH_REMATCH[2]}"
        fi
    done < "$temp_file"

    rm -f "$temp_file"
    
    # 检查是否有有效用户数据
    if [[ ${#users[@]} -eq 0 ]]; then
        echo -e "${RED}错误：未加载有效用户数据${NC}"
        exit 1
    fi
}

# 检查密码是否过期
check_password_expiry() {
    local username=$1
    local user_info=${users[$username]}
    
    IFS='|' read -ra parts <<< "$user_info"
    local hashed_password=${parts[0]}
    local create_date=${parts[1]}
    local valid_months=${parts[2]}
    
    # 如果有效月数为0，则密码长期有效
    if [[ $valid_months -eq 0 ]]; then
        return 0
    fi
    
    # 计算过期日期
    local expire_date=$(date -d "$create_date + $valid_months months" +%Y-%m-%d)
    local today=$(date +%Y-%m-%d)
    
    # 比较日期
    if [[ $today > $expire_date ]]; then
        echo -e "${RED}密码已过期！请更换密码。${NC}"
        return 1
    else
        echo -e "密码有效期至: ${GREEN}$expire_date${NC}"
        return 0
    fi
}

# 检测是否已配置用户和密码
check_config() {
    if [[ -f "$CONFIG_FILE" ]]; then
        # 安全读取加密配置文件
        local encrypted_content=$(safe_read_file "$CONFIG_FILE")
        
        if [[ -z "$encrypted_content" ]]; then
            echo -e "${RED}配置文件为空或无法读取${NC}"
            return 1
        fi
        
        # 解密内容
        local decrypted_content=$(decrypt_data "$encrypted_content")
        
        if [[ -z "$decrypted_content" ]]; then
            echo -e "${RED}配置文件解密失败${NC}"
            return 1
        fi
        
        # 解析解密后的内容
        local signature=$(echo "$decrypted_content" | grep '^signature=' | cut -d'=' -f2- | tr -d '\0')
        local config_data=$(echo "$decrypted_content" | grep -v '^signature=' | tr -d '\0')
        
        if [[ -z "$signature" || -z "$config_data" ]]; then
            echo -e "${RED}配置文件格式错误${NC}"
            return 1
        fi
        
        # 验证数字签名
        if ! verify_signature "$config_data" "$signature"; then
            echo -e "${RED}配置文件签名验证失败，可能被篡改${NC}"
            return 1
        fi
        
        # 解析配置数据
        local saved_username=$(echo "$config_data" | grep '^username=' | cut -d'=' -f2- | tr -d '\0')
        local saved_password_hash=$(echo "$config_data" | grep '^password_hash=' | cut -d'=' -f2- | tr -d '\0')
        local saved_create_date=$(echo "$config_data" | grep '^create_date=' | cut -d'=' -f2- | tr -d '\0')
        local saved_valid_months=$(echo "$config_data" | grep '^valid_months=' | cut -d'=' -f2- | tr -d '\0')

        if [[ -n "$saved_username" && -n "$saved_password_hash" ]]; then
            # 检查用户是否存在
            if [[ -n "${users[$saved_username]}" ]]; then
                # 获取存储的用户信息
                local user_info=${users[$saved_username]}
                IFS='|' read -ra parts <<< "$user_info"
                local stored_hash=${parts[0]}
                
                # 验证密码哈希
                if [[ "$stored_hash" == "$saved_password_hash" ]]; then
                    # 检查密码有效期
                    if check_password_expiry "$saved_username"; then
                        echo -e "${GREEN}检测到已配置用户：$saved_username，跳过登录验证。${NC}"
                        return 0
                    else
                        echo -e "${RED}密码已过期，需要重新登录。${NC}"
                        return 1
                    fi
                else
                    echo -e "${RED}配置文件中密码不匹配，需要重新登录。${NC}"
                    return 1
                fi
            else
                echo -e "${RED}配置文件中用户不存在，需要重新登录。${NC}"
                return 1
            fi
        else
            echo -e "${RED}配置文件不完整，需要登录。${NC}"
            return 1
        fi
    else
        echo -e "${YELLOW}未检测到配置文件，需要登录。${NC}"
        return 1
    fi
}

# 用户登录
user_login() {
    local username password hashed_password
    read -p "请输入用户名: " username
    read -sp "请输入密码: " password
    echo

    # 标准化输入用户名
    username=$(echo "$username" | tr -d '"'"'" | tr -d '\0')

    if [[ -z "${users[$username]}" ]]; then
        echo -e "${RED}错误：用户名 '$username' 不存在！${NC}"
        return 1
    fi
    
    # 获取用户信息
    local user_info=${users[$username]}
    IFS='|' read -ra parts <<< "$user_info"
    local stored_hash=${parts[0]}
    
    # 计算输入密码的哈希值
    hashed_password=$(echo -n "$password" | sha256sum | awk '{print $1}' | tr -d '\0')

    # 验证密码
    if [[ "$stored_hash" == "$hashed_password" ]]; then
        # 检查密码有效期
        if check_password_expiry "$username"; then
            echo -e "${GREEN}登录成功！${NC}"
            update_config "$username" "$hashed_password"
            return 0
        else
            return 1
        fi
    else
        echo -e "${RED}密码错误！${NC}"
        return 1
    fi
}

# 更新配置文件
update_config() {
    local username="$1"
    local hashed_password="$2"
    local user_info=${users[$username]}
    
    IFS='|' read -ra parts <<< "$user_info"
    local create_date=${parts[1]}
    local valid_months=${parts[2]}
    
    # 构建配置内容
    local config_content="username=$username
password_hash=$hashed_password
create_date=$create_date
valid_months=$valid_months"
    
    # 生成数字签名
    local signature=$(generate_signature "$config_content")
    
    # 添加签名到配置内容
    local signed_content="${config_content}
signature=${signature}"
    
    # 加密并保存配置
    local encrypted_content=$(encrypt_data "$signed_content")
    safe_write_file "$encrypted_content" "$CONFIG_FILE"
    
    # 设置严格的文件权限
    chmod 600 "$CONFIG_FILE"
    chown root:root "$CONFIG_FILE"
    
    echo -e "${GREEN}用户配置已安全更新！${NC}"
}

# 执行升级操作
execute_upgrade() {
    local markers=("$@")
    local URL="https://gitee.com/banban1000/docker/raw/master/oa/allupdate.sh"
    local TEMP_FILE=$(mktemp)
    local EXTRACTED_FILE="${TEMP_FILE}.extracted"

    > "$EXTRACTED_FILE"

    for ((i = 0; i < ${#markers[@]}; i += 2)); do
        start_marker=${markers[i]}
        end_marker=${markers[i+1]}
        curl -fsSL "$URL" | sed -n -e "/$start_marker/,/$end_marker/p" >> "$EXTRACTED_FILE"
    done

    if [ -s "$EXTRACTED_FILE" ]; then
        bash "$EXTRACTED_FILE"
    else
        echo -e "${RED}未提取到指定标记之间的内容。${NC}"
    fi

    rm -f "$TEMP_FILE" "$EXTRACTED_FILE"
}

# 显示升级菜单
show_upgrade_menu() {
    # 设置升级选项
    local upgrade_options=(
        "升级全部" 
        "升级oa" 
        "升级blog" 
        "升级dev" 
        "还原升级后数据" 
        "还原升级前数据" 
        "还原上次19:30备份数据" 
        "还原其它机器备份的数据" 
        "composer重新安装或升级" 
        "取消升级还原"
    )

    # 显示升级选项
    echo -e "\n${GREEN}请选择升级选项：${NC}"
    local index=1
    for option in "${upgrade_options[@]}"; do
        echo "$index) $option"
        index=$((index+1))
    done

    # 设置计时器开始时间
    local start_time=$(date +%s)

    # 等待用户输入
    echo -e "\n${YELLOW}请60秒内选择上面对应升级选项,不选择则自动退出.${NC}"
    read -t 60 -p "请选择(1-10): " user_choice

    # 60秒内没有输入，执行自动退出
    if [ -z "$user_choice" ]; then
        echo -e "${RED}60秒内没有选择，退出中......${NC}"
        exit 1
    else
        case $user_choice in
            1)
                echo -e "${GREEN}升级全部.......${NC}"
                execute_upgrade "start1" "end1" "start2" "end2" "start3" "end3" "start4" "end4" "start5" "end5"
                ;;
            2)
                echo -e "${GREEN}升级oa.......${NC}"
                execute_upgrade "start1" "end1" "start2" "end2" "start5" "end5"
                ;;
            3)
                echo -e "${GREEN}升级blog......${NC}"
                execute_upgrade "start1" "end1" "start3" "end3" "start5" "end5"
                ;;
            4)
                echo -e "${GREEN}升级dev......${NC}"
                execute_upgrade "start1" "end1" "start4" "end4" "start5" "end5"
                ;;
            5)
                echo -e "${GREEN}还原升级后数据中...${NC}"
                execute_upgrade "start6" "end6"
                ;;
            6)
                echo -e "${GREEN}还原升级前数据中......${NC}"
                execute_upgrade "start7" "end7"
                ;;
            7)
                echo -e "${GREEN}还原上次19：30备份数据中......${NC}"
                execute_upgrade "start8" "end8"
                ;;
            8)
                echo -e "${GREEN}还原其它机器备份的数据中......${NC}"
                echo -e "${YELLOW}↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓${NC}"
                execute_upgrade "start9" "end9"
                ;;
            9)
                echo -e "${GREEN}composer 重新安装或升级${NC}"
                bash -c "$(curl -fsSL https://gitee.com/banban1000/docker/raw/master/oa/allcomposerinstall.sh)"
                ;;
            10)
                echo -e "${GREEN}取消升级还原，退出中......${NC}"
                exit 0
                ;;
            *)
                echo -e "${RED}无效的选择.${NC}"
                exit 1
                ;;
        esac
    fi
}

# 主程序流程
init_env
check_root
load_users

# 先尝试检查本地配置
if ! check_config; then
    # 本地配置检查失败，要求登录
    if ! user_login; then
        echo -e "${RED}登录失败，程序退出。${NC}"
        exit 1
    fi
fi

# 显示升级菜单
show_upgrade_menu

exit 0